 /*******************************************************************************
  * Copyright (c) 2000, 2004 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.test.performance;

 import junit.framework.TestCase;

 import org.eclipse.core.runtime.Platform;

 import org.eclipse.test.internal.performance.InternalDimensions;
 import org.eclipse.test.internal.performance.InternalPerformanceMeter;
 import org.eclipse.test.internal.performance.NullPerformanceMeter;
 import org.eclipse.test.internal.performance.OSPerformanceMeterFactory;
 import org.eclipse.test.internal.performance.PerformanceMeterFactory;
 import org.eclipse.test.internal.performance.PerformanceTestPlugin;
 import org.eclipse.test.internal.performance.data.Dim;
 import org.eclipse.test.internal.performance.eval.AbsoluteBandChecker;
 import org.eclipse.test.internal.performance.eval.AssertChecker;
 import org.eclipse.test.internal.performance.eval.Evaluator;
 import org.eclipse.test.internal.performance.eval.IEvaluator;
 import org.eclipse.test.internal.performance.eval.RelativeBandChecker;
 import org.osgi.framework.Bundle;

 /**
  * Helper for performance measurements. Currently provides performance meter
  * creation and checking of measurements.
  *
  * This class is not intended to be subclassed by clients.
  *
  * @since 3.1
  */
 public class Performance {
     
     /**
      * A comment kind of a comment that explains a performance degradation.
      */
     public static final int EXPLAINS_DEGRADATION_COMMENT= 1;

     private static final String PERFORMANCE_METER_FACTORY= "/option/performanceMeterFactory"; //$NON-NLS-1$
 private static final String PERFORMANCE_METER_FACTORY_PROPERTY= "PerformanceMeterFactory"; //$NON-NLS-1$

     private static Performance fgDefault;
     
     private PerformanceMeterFactory fPerformanceMeterFactory;
     private IEvaluator fDefaultEvaluator;

     /** Null performance meter singleton */
     private NullPerformanceMeter fNullPeformanceMeter;
     

     /**
      * Private constructor to block instance creation.
      */
     private Performance() {
         // empty
 }
     
     /**
      * Returns the singleton of <code>Performance</code>
      *
      * @return the singleton of <code>Performance</code>
      */
     public static Performance getDefault() {
         if (fgDefault == null)
             fgDefault= new Performance();
         return fgDefault;
     }
     
     /**
      * Asserts default properties of the measurements captured by the given
      * performance meter.
      *
      * @param performanceMeter the performance meter
      * @throws RuntimeException if the properties do not hold
      */
     public void assertPerformance(PerformanceMeter performanceMeter) {
         if (fDefaultEvaluator == null) {
             fDefaultEvaluator= new Evaluator();
             fDefaultEvaluator.setAssertCheckers(new AssertChecker[] {
                     new RelativeBandChecker(InternalDimensions.ELAPSED_PROCESS, 0.0f, 1.10f),
                     //new RelativeBandChecker(InternalDimensions.CPU_TIME, 0.0f, 1.10f),
 //new RelativeBandChecker(InternalDimensions.WORKING_SET, 0.0f, 3.00f),
 //new RelativeBandChecker(InternalDimensions.USED_JAVA_HEAP, 0.0f, 2.00f),
 //new RelativeBandChecker(InternalDimensions.SYSTEM_TIME, 0.0f, 1.10f)
 });
         }
         fDefaultEvaluator.evaluate(performanceMeter);
     }

     /**
      * Asserts that the measurement specified by the dimension captured in the given
      * performance meter is within a certain range with respect to some reference value.
      * If the performance meter doesn't provide the specified dimension, the call has no effect.
      *
      * @param performanceMeter the performance meter
      * @param dim the Dimension to check
      * @param lowerPercentage a negative number indicating the percentage the measured value is allowed to be smaller than some reference value
      * @param upperPercentage a positive number indicating the percentage the measured value is allowed to be greater than some reference value
      * @throws RuntimeException if the properties do not hold
      */
     public void assertPerformanceInRelativeBand(PerformanceMeter performanceMeter, Dimension dim, int lowerPercentage, int upperPercentage) {
         Evaluator e= new Evaluator();
         e.setAssertCheckers(new AssertChecker[] {
                 new RelativeBandChecker((Dim) dim, 1.0+(lowerPercentage / 100.0), 1.0+(upperPercentage / 100.0)),
         });
         e.evaluate(performanceMeter);
     }

     /**
      * Asserts that the measurement specified by the dimension captured in the given
      * performance meter is within a certain range with respect to some reference value.
      * If the performance meter doesn't provide the specified dimension, the call has no effect.
      *
      * @param performanceMeter the performance meter
      * @param dim the Dimension to check
      * @param lowerBand a negative number indicating the absolute amount the measured value is allowed to be smaller than some reference value
      * @param upperBand a positive number indicating the absolute amount the measured value is allowed to be greater than some reference value
      * @throws RuntimeException if the properties do not hold
      */
     public void assertPerformanceInAbsoluteBand(PerformanceMeter performanceMeter, Dimension dim, int lowerBand, int upperBand) {
         Evaluator e= new Evaluator();
         e.setAssertCheckers(new AssertChecker[] {
                 new AbsoluteBandChecker((Dim) dim, lowerBand, upperBand),
         });
         e.evaluate(performanceMeter);
     }

     /**
      * Creates a performance meter for the given scenario id.
      *
      * @param scenarioId the scenario id
      * @return a performance meter for the given scenario id
      * @throws IllegalArgumentException if a performance meter for the given
      * scenario id has already been created
      */
     public PerformanceMeter createPerformanceMeter(String scenarioId) {
         return getPeformanceMeterFactory().createPerformanceMeter(scenarioId);
     }

     /**
      * Returns the null performance meter singleton.
      *
      * @return the null performance meter singleton
      */
     public PerformanceMeter getNullPerformanceMeter() {
         if (fNullPeformanceMeter == null)
             fNullPeformanceMeter= new NullPerformanceMeter();
         return fNullPeformanceMeter;
     }

     /**
      * Returns a default scenario id for the given test. The test's name
      * must have been set, such that <code>test.getName()</code> is not
      * <code>null</code>.
      *
      * @param test the test
      * @return the default scenario id for the test
      */
     public String getDefaultScenarioId(TestCase test) {
         return test.getClass().getName() + '#' + test.getName() + "()"; //$NON-NLS-1$
 }
     
     /**
      * Returns a default scenario id for the given test and id. The test's
      * name must have been set, such that <code>test.getName()</code> is
      * not <code>null</code>. The id distinguishes multiple scenarios in
      * the same test.
      *
      * @param test the test
      * @param id the id
      * @return the default scenario id for the test and the id
      */
     public String getDefaultScenarioId(TestCase test, String id) {
         return getDefaultScenarioId(test) + '-' + id;
     }

     private PerformanceMeterFactory getPeformanceMeterFactory() {
         if (fPerformanceMeterFactory == null)
             fPerformanceMeterFactory= createPerformanceMeterFactory();
         return fPerformanceMeterFactory;
     }
     
     private PerformanceMeterFactory createPerformanceMeterFactory() {
         PerformanceMeterFactory factory;
         factory= tryInstantiate(System.getProperty(PERFORMANCE_METER_FACTORY_PROPERTY));
         if (factory != null)
             return factory;
         
         factory= tryInstantiate(Platform.getDebugOption(PerformanceTestPlugin.PLUGIN_ID + PERFORMANCE_METER_FACTORY));
         if (factory != null)
             return factory;
         
         return createDefaultPerformanceMeterFactory();
     }
     
     private PerformanceMeterFactory tryInstantiate(String className) {
         PerformanceMeterFactory instance= null;
         if (className != null && className.length() > 0) {
             try {
                 int separator= className.indexOf(':');
                 Bundle bundle= null;
                 if (separator == -1) {
                     bundle= PerformanceTestPlugin.getDefault().getBundle();
                 } else {
                     String bundleName= className.substring(0, separator);
                     className= className.substring(separator + 1);
                     bundle= Platform.getBundle(bundleName);
                 }
                 Class c= bundle.loadClass(className);
                 instance= (PerformanceMeterFactory) c.newInstance();
             } catch (ClassNotFoundException e) {
                 PerformanceTestPlugin.log(e);
             } catch (InstantiationException e) {
                 PerformanceTestPlugin.log(e);
             } catch (IllegalAccessException e) {
                 PerformanceTestPlugin.log(e);
             } catch (ClassCastException e) {
                 PerformanceTestPlugin.log(e);
             }
         }
         return instance;
     }

     private PerformanceMeterFactory createDefaultPerformanceMeterFactory() {
         return new OSPerformanceMeterFactory();
     }
     
     /**
      * Mark the scenario represented by the given PerformanceMeter
      * to be included into the global and the component performance summary. The summary shows
      * the given dimension of the scenario and labels the scenario with the short name.
      *
      * @param pm the PerformanceMeter
      * @param shortName a short (shorter than 40 characters) descriptive name of the scenario
      * @param dimension the dimension to show in the summary
      */
     public void tagAsGlobalSummary(PerformanceMeter pm, String shortName, Dimension dimension) {
         tagAsGlobalSummary(pm, shortName, new Dimension[] { dimension } );
     }

     /**
      * Mark the scenario represented by the given PerformanceMeter
      * to be included into the global and the component performance summary. The summary shows
      * the given dimensions of the scenario and labels the scenario with the short name.
      *
      * @param pm the PerformanceMeter
      * @param shortName a short (shorter than 40 characters) descriptive name of the scenario
      * @param dimensions an array of dimensions to show in the summary
      */
     public void tagAsGlobalSummary(PerformanceMeter pm, String shortName, Dimension[] dimensions) {
         if (pm instanceof InternalPerformanceMeter) {
             InternalPerformanceMeter ipm= (InternalPerformanceMeter) pm;
             ipm.tagAsSummary(true, shortName, dimensions);
         }
     }

     
     /**
      * Mark the scenario represented by the given PerformanceMeter
      * to be included into the component performance summary. The summary shows
      * the given dimension of the scenario and labels the scenario with the short name.
      *
      * @param pm the PerformanceMeter
      * @param shortName a short (shorter than 40 characters) descriptive name of the scenario
      * @param dimension the dimension to show in the summary
      */
     public void tagAsSummary(PerformanceMeter pm, String shortName, Dimension dimension) {
         tagAsSummary(pm, shortName, new Dimension[] { dimension } );
     }

     /**
      * Mark the scenario represented by the given PerformanceMeter
      * to be included into the component performance summary. The summary shows
      * the given dimensions of the scenario and labels the scenario with the short name.
      *
      * @param pm the PerformanceMeter
      * @param shortName a short (shorter than 40 characters) descriptive name of the scenario
      * @param dimensions an array of dimensions to show in the summary
      */
     public void tagAsSummary(PerformanceMeter pm, String shortName, Dimension[] dimensions) {
         if (pm instanceof InternalPerformanceMeter) {
             InternalPerformanceMeter ipm= (InternalPerformanceMeter) pm;
             ipm.tagAsSummary(false, shortName, dimensions);
         }
     }

     /**
      * Set a comment for the scenario represented by the given PerformanceMeter.
      * Currently only comments with a commentKind of EXPLAINS_DEGRADATION_COMMENT are used.
      * Their commentText is shown in a hover of the performance summaries graph if a performance
      * degradation exists.
      *
      * @param pm the PerformanceMeter
      * @param commentKind kind of comment. Must be EXPLAINS_DEGRADATION_COMMENT to have an effect.
      * @param commentText the comment (shorter than 400 characters)
      */
     public void setComment(PerformanceMeter pm, int commentKind, String commentText) {
         if (commentKind == EXPLAINS_DEGRADATION_COMMENT) {
             if (pm instanceof InternalPerformanceMeter) {
                 InternalPerformanceMeter ipm= (InternalPerformanceMeter) pm;
                 ipm.setComment(commentKind, commentText);
             }
         }
     }
 }

